package com.demo.gateway.common.filter;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.exception.DisableServiceException;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.dev33.satoken.reactor.filter.SaReactorFilter;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.demo.common.core.constants.HttpStatus;
import com.demo.common.core.domain.LoginUser;
import com.demo.common.redis.constants.CacheConstants;
import com.demo.common.redis.service.RedisService;
import com.demo.gateway.config.properties.IgnoreWhiteProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.TimeUnit;

/**
 * [Sa-Token 权限认证] 配置类
 *
 * @author click33
 */
@Configuration
public class SaTokenConfigure {


    /**
     * 排除过滤的 uri 地址，nacos自行添加
     */
    @Autowired
    private IgnoreWhiteProperties ignoreWhite;

    @Value("${sa-token.timeout}")
    private long tokenTimeout;

    @Autowired
    private RedisService redisService;

    protected static final long MILLIS_SECOND = 1000;

    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

    private final static Long MILLIS_MINUTE_TEN = 120 * MILLIS_MINUTE;


    /**
     * 注册 Sa-Token全局过滤器
     */
    @Bean
    public SaReactorFilter getSaReactorFilter() {
        return new SaReactorFilter()
                // 拦截地址
                // 拦截全部path
                .addInclude("/**")
                // 开放地址
                .setExcludeList(ignoreWhite.getWhites())
                // 鉴权方法：每次访问进入
                .setAuth(obj -> {
                    // 登录校验 -- 拦截所有路由
                    SaRouter.match("/**", r -> StpUtil.checkLogin());
                    //每次调用就刷新token
//                    StpUtil.renewTimeout(tokenTimeout);
                    //刷新缓存用户信息
//                    verifyToken(StpUtil.getLoginId().toString());
                    // 权限认证 -- 不同模块, 校验不同权限
//                    SaRouter.match("/user/**", r -> StpUtil.checkPermission("user"));
//                    SaRouter.match("/user/**", r -> StpUtil.checkRole("admin"));
                    // 更多匹配 ...  */
                })
                // 前置函数：在每次认证函数之前执行
                .setBeforeAuth(obj -> {
                    // ---------- 设置跨域响应头 ----------
                    SaHolder.getResponse()
                            // 允许指定域访问跨域资源
                            .setHeader("Access-Control-Allow-Origin", "*")
                            // 允许所有请求方式
                            .setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE")
                            // 有效时间
                            .setHeader("Access-Control-Max-Age", "3600")
                            // 允许的header参数
                            .setHeader("Access-Control-Allow-Headers", "*");
                })
                // 异常处理方法：每次setAuth函数出现异常时进入
                .setError(e -> {
                    // 返回给前端的数据
                    if (e instanceof NotLoginException) {
                        switch (((NotLoginException) e).getType()) {
                            case "-2":
                            case "-3":
                                return SaResult.error("token失效").setCode(HttpStatus.FORBIDDEN);
                            case "-4":
                            case "-5":
                                return SaResult.error("账号已在别处登录/下线").setCode(HttpStatus.FORBIDDEN);
                            default:
                                return SaResult.error("当前会话未登录").setCode(HttpStatus.UNAUTHORIZED);
                        }
                    }
                    if (e instanceof NotRoleException) {
                        return SaResult.error("没有权限访问").setCode(HttpStatus.WARN);
                    }
                    if (e instanceof NotPermissionException) {
                        return SaResult.error("没有权限访问").setCode(HttpStatus.WARN);
                    }
                    if (e instanceof DisableServiceException) {
                        return SaResult.error("账号已被封禁").setCode(HttpStatus.WARN);
                    }
                    return SaResult.error(e.getMessage());
                });
    }

    /**
     * 验证令牌有效期，相差不足120分钟，自动刷新缓存
     *
     * @param loginId
     */
    public void verifyToken(String loginId) {
        String userKey = CacheConstants.LOGIN_USER_KEY + loginId;
        LoginUser loginUser = redisService.getCacheObject(userKey);
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        if (expireTime - currentTime <= MILLIS_MINUTE_TEN) {
            refreshToken(userKey, loginUser);
        }
    }

    /**
     * 刷新令牌有效期
     *
     * @param loginUser 登录信息
     */
    public void refreshToken(String userKey, LoginUser loginUser) {
        redisService.setCacheObject(userKey, loginUser, tokenTimeout, TimeUnit.SECONDS);
        loginUser.setExpireTime(System.currentTimeMillis() + tokenTimeout * MILLIS_SECOND);
    }
}

